package Game;


import Cards.Deck;
import Cards.UnoCard;
import Internationalization.LoadInternationalization;
import JDBC.JavaDataBase;
import Resources1.Settings;
import UnoBots.BotPlayerUNO;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.ListIterator;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * Class UnoGame. It manages UnoGame
 */

public class UnoGame extends Game<UnoCard, PlayerUNO> {
    private int cardsToTake; //Variable estática que almacena la acumulación de las cartas por robar.
    private UnoCard.Colour colour;
    private boolean robado;
    private Map<PlayerUNO, Integer> playersPoints;
    private JavaDataBase jdb;
    /**
     * Constructor of this class.
     * Builds the main structures necessaries to play UNO
     */
    public UnoGame(){
        super();
        cardsToTake = 0;
        robado = false;
        playersPoints= new HashMap<>();
        jdb = new JavaDataBase();
        PlayerUNO pAux;
        BotPlayerUNO bAux;
        ArrayList<String> jugadores = new ArrayList<>();
        ArrayList<String> bots = new ArrayList<>();
        //Cargamos la baraja
        loadDeck();
        try{
            jugadores = Settings.getPlayersUNO();
            bots = Settings.getBotsUNO();
        }
        catch(Exception e){
            System.out.println(e.toString());
        }
        try{
            for (int i = 0; i<jugadores.size(); i++){
                pAux = new PlayerUNO(jugadores.get(i));
                players.add(pAux);
                jdb.insertPlayer(pAux.getId(), "PLAYER");
            }
            for (int i = 0; i<bots.size(); i++){
                bAux = BotPlayerUNO.getInstance(bots.get(i), i);
                players.add(bAux);
                jdb.insertPlayer(bAux.getId(), "BOT");
            }
            jdb.insertStart(players.size(),"UNO");
//            jdb.disconnect();
        }
        catch (SQLException ex){
            System.out.println(ex);
        }
        //jdb.insertarJugador(players.get(0).getId(), "PLAYER");
    }
    
    /**
    * This method starts and controls the game.
    * Checks what card has been played, and what effect did it cause on the game.
    * Differences wether if it is a normal, special or joker card and applies the correspondant effect
    * to the correct player.
    */
    
    public void startGame(){
        ListIterator<PlayerUNO> it = players.listIterator();
        boolean reverse = false;
        PlayerUNO player = new PlayerUNO(""); // Se inicializa solo porque se queja el compilador
        dealHand();
        //Colocamos una carta en lo alto de la pila
        playedCards.add(deck.takeCard());
        //Comprobamos que no sea roba 4, en cuyo caso se sacará otra
        while (playedCards.getFirst().getRank().equals(UnoCard.Number.TAKEFOUR)){
            deck.add(deck.size()/2, playedCards.getFirst());
            playedCards.removeFirst();
            playedCards.add(deck.takeCard());
        }
        //Si es un "Roba2" el primer jugador tendrá 2 cartas pendientes por robar.
        if (playedCards.getFirst().getRank().equals(UnoCard.Number.TAKETWO)){
            cardsToTake += 2;
        }
        if (playedCards.getFirst().getRank().equals(UnoCard.Number.COLOURCHANGE)){
            player = it.next();
            player.changeColour();
            it.previous();
        }
        while (!endGame()){
            //Si los turnos van en sentido contrario
            if (reverse){
                if (!it.hasPrevious()){
                    it = players.listIterator(players.size());
                }
                player = it.previous();
            }
            //Si los turnos van en sentido normal
            else if (!reverse){
                if (!it.hasNext()){
                    it = players.listIterator();
                }
                player = it.next();
            }
            //System.out.println("Se ha jugado la carta: " + this.getTopCard());
            System.out.println(LoadInternationalization.getRB().getString("playedCard") +getTopCard());
            if (this.getTopCard().getRank().equals(UnoCard.Number.COLOURCHANGE)){
                System.out.println(LoadInternationalization.getRB().getString("chosenColour") + colour);
            }
            player.myTurn(this);
            //Controlar aquí la carta que se ha jugado, en caso de que sea especial
            //o comodín hacer el efecto correspondiente.
            if (this.getTopCard().isJoker() || this.getTopCard().isSpecialCard() ){
                switch (this.getTopCard().getRank()){
                    case TAKETWO:
                        if (!robado){
                            cardsToTake +=2;
                            colour = getTopCard().getSuit();
                        }
                        break;
                    case REVERSE:
                        if (!reverse){
                            player = it.previous();
                            reverse = true;
                        }
                        else{
                            player = it.next();
                            reverse = false;
                        }
                        colour = getTopCard().getSuit();
                        break;
                    case NEXT:
                        if (reverse){
                            if (!it.hasPrevious()){
                                it = players.listIterator(players.size());
                            }
                            it.previous();
                        }
                        else{
                            if (!it.hasNext()){
                                it = players.listIterator();
                            }
                            it.next();
                        }
                        colour = getTopCard().getSuit();
                        break;
                    case TAKEFOUR:
                        if (!robado){
                            cardsToTake += 4;
                            setRobado(false);
                            colour = player.changeColour();
                            System.out.println(LoadInternationalization.getRB().getString("colourChanged") + this.colour);
                        }
                        break;
                    case COLOURCHANGE:
                        colour = player.changeColour();
                        break;
                    default:
                        System.err.println(LoadInternationalization.getRB().getString("error"));
                        break;
                }
            }
            else{
                colour = getTopCard().getSuit();
            }
        }
        countPoints();
    }
    /**
    * This method check if the Game has ended
    * @return true if the Game has ended or false in other case
    */
    private boolean endGame(){
        if (deck.isEmpty()){
            return true;
        }
        else{
            for (PlayerUNO p : players){
                if (p.handIsEmpty()){
                    return true;
                }
            }
            return false;
        }
    }
    
    /**
    * This method counts the Points of each player and store them on a HashMap<Player, Integer>
    */
    private void countPoints(){
        int acum = 0;
//        jdb = new JavaDataBase();
        for (PlayerUNO p : players){
            for (UnoCard c : p.getHand()){
                acum += c.points();
            }
            playersPoints.put(p, acum);
            if (acum == 0)
                jdb.insertGame(p.getId(), 1);
            else
                jdb.insertGame(p.getId(), 0);
            acum = 0;
        }
        System.out.println(LoadInternationalization.getRB().getString("pointsFollows"));
        System.out.println(playersPoints);
        
        jdb.disconnect();
    }
    
    /**
     * This method returns the number of cards that a player has to draw
     *@return cardsToTake 
     */
    public int getCardsToTake(){
        return cardsToTake;
    }

    /**
     * This method changes the number of cards that a players has to draw.
     * @param cardsToTake
     */
    
    public void setCardsToTake(int cardsToTake){
        this.cardsToTake = cardsToTake;
    }
    
    /**
     * This method get the current played colour.
     * @return colour 
     */
    public UnoCard.Colour getColour(){
        return colour;
    }
    
    /**
     * This method set that the last player has taken a card
     * @param b
     */
    
    public void setRobado(boolean b){
        this.robado = b;
    }
    
    /**
     * This method check if the last player has drawn a card
     *@return robado 
     */
    
    public boolean getRobado(){
        return robado;
    }
    
    /**
     *This method get the number of players
     *@return players.size() 
     */
    
    public int getNumberOfPlayers(){
//        System.out.println("PLAYERS VALE: " + players.size());
        return players.size();
    }
    
    /** This method returns a HashMap that contains each player with their points.
     * @return playersPoints
     * 
    */
    public Map<PlayerUNO, Integer> getPlayersPoints(){
        return playersPoints;
    }
    
     /**
     * This method loads the deck from a file and shuflles it.
     */
    private void loadDeck(){
        BufferedReader br = null;
        try {
            Deck<UnoCard> d = new Deck<>();
            UnoCard uc;
            String[] card;
            String str;
            InputStream is = getClass().getResourceAsStream("/Resources1/unoDeck.txt");
            br = new BufferedReader(new InputStreamReader(is));
            while ((str = br.readLine()) != null){
                card = str.split("\\s");
                uc = new UnoCard(UnoCard.Number.valueOf(card[0]), UnoCard.Colour.valueOf(card[1]));
                d.add(uc);
            }
            Collections.shuffle(d);
            this.deck = d;
            
            //En this.deck tenemos almacenada la baraja completa barajada.
        } catch (IOException ex) {
            System.err.println(ex.toString());
        }
        finally{
            try {
                if (br!= null)
                    br.close();
            } catch (IOException ex) {
                Logger.getLogger(UnoGame.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }
    
     /**
     * This method deal a 7 cards hand to each player in the current game.
     * Removes the dealt cards from the deck.
     */
    
    private void dealHand(){
        //Repartimos las cartas a cada jugador
        for (PlayerUNO p : players){
            for (int i = 0; i<7; i++){
                p.addCardToHand(deck.takeCard());
            }
        }
    }
}
